package com.cj.kvstore

import android.content.Context
import android.os.Parcel
import android.os.Parcelable
import android.util.Log
import java.io.Serializable

class KVStore {

    companion object {
        // Used to load the 'native-lib' library on application startup.
        init {
            System.loadLibrary("lib-kvstore")
        }

        private val mCreators = HashMap<String, Parcelable.Creator<*>>()

        private var rootDir:String? = null

        fun initialize(context: Context):String{
            val root = context.filesDir.absolutePath + "/kvstore"
            return initialize(root)
        }

        fun initialize(rootDir:String):String{
            KVStore.rootDir = rootDir
            jniInitialize(rootDir)
            return rootDir
        }

        /**
         * 实例化，物理内存产生映射
         */
        fun defaultKVStore():KVStore{
            val handle = getDefaultKVStore()
            return KVStore(handle)
        }

        fun kvStoreWithID(name:String):KVStore{
            val handle = getKVStoreWithID(name)
            return KVStore(handle)
        }

        @JvmStatic
        private external fun jniInitialize(rootDir: String)

        @JvmStatic
        private external fun getDefaultKVStore():Long

        @JvmStatic
        private external fun getKVStoreWithID(name:String):Long

    }

    constructor(handle:Long){
        this.nativeHandle = handle
    }

    private val nativeHandle:Long

    fun putInt(key:String,value:Int){
        putInt(nativeHandle,key,value)
    }

    fun getInt(key: String,defaultValue:Int):Int{
        return getInt(nativeHandle,key, defaultValue)
    }

    fun putString(key:String,value:String?){
        putString(nativeHandle,key,value)
    }

    fun getString(key: String):String?{
        return getString(nativeHandle,key)
    }

    fun putDouble(key:String,value:Double){
        putDouble(nativeHandle,key,value)
    }

    fun getDouble(key: String,defaultValue:Double):Double{
        return getDouble(nativeHandle,key, defaultValue)
    }

    fun putBoolean(key:String,value:Boolean){
        putBoolean(nativeHandle,key,value)
    }

    fun getBoolean(key: String,defaultValue:Boolean):Boolean{
        return getBoolean(nativeHandle,key, defaultValue)
    }

    fun removeValueFromKey(key: String){
        removeValueFromKey(nativeHandle,key)
    }

    fun containsKey(key: String):Boolean{
        return containsKey(nativeHandle,key)
    }

    fun getBytes(key:String):ByteArray?{
        return getBytes(nativeHandle,key)
    }

    fun putBytes(key:String,value:ByteArray?){
        putBytes(nativeHandle,key,value)
    }

    fun putParcelable(key: String,value: Parcelable?){
        if(value == null){
            putBytes(nativeHandle,key,null)
            return
        }
        val source = Parcel.obtain()
        value.writeToParcel(source, 0)
        val bytes = source.marshall()
        source.recycle()
        putBytes(nativeHandle,key,bytes)
    }

    fun <T : Parcelable>getParcelable(key: String,tClass:Class<T>,defaultValue:T? = null):T?{
        val bytes = getBytes(nativeHandle,key) ?: return defaultValue

        val source = Parcel.obtain()
        source.unmarshall(bytes, 0, bytes.size)
        source.setDataPosition(0)

        try {
            val name = tClass.toString()
            var creator: Parcelable.Creator<T>?
            synchronized(mCreators) {
                creator = mCreators[name] as Parcelable.Creator<T>?
                if (creator == null) {
                    val f = tClass.getField("CREATOR")
                    creator = f[null] as Parcelable.Creator<T>
                    if (creator != null) {
                        mCreators[name] = creator!!
                    }
                }
            }
            return if (creator != null) {
                creator!!.createFromParcel(source)
            } else {
                throw Exception(
                    "Parcelable protocol requires a "
                            + "non-null static Parcelable.Creator object called "
                            + "CREATOR on class " + name
                )
            }
        } catch (e: Exception) {
            Log.e("KVStore", e.toString())
        } finally {
            source.recycle()
        }
        return defaultValue
    }

    fun putSerializable(key: String,value: Serializable?){
        if(value == null){
            putBytes(nativeHandle,key,null)
            return
        }
        val bytes = ObjectSerializer.toByteArray(value)
        if(bytes == null){
            putBytes(nativeHandle,key,null)
            return
        }
        putBytes(nativeHandle,key,bytes)
    }

    fun <T : Serializable>getSerializable(key: String,defaultValue:T? = null):T?{
        val bytes = getBytes(nativeHandle,key) ?: return defaultValue
        return ObjectSerializer.fromByteArray<T>(bytes)
    }

    fun clearAll(){
        clear(nativeHandle)
    }


    private external fun putInt(handle: Long,key:String,value:Int)
    private external fun getInt(handle: Long,key:String,defaultValue:Int):Int
    private external fun getString(handle: Long,key:String):String?
    private external fun putString(handle: Long,key:String,value:String?)
    private external fun putDouble(handle: Long,key:String,value:Double)
    private external fun getDouble(handle: Long,key:String,defaultValue:Double):Double
    private external fun putBoolean(handle: Long,key:String,value:Boolean)
    private external fun getBoolean(handle: Long,key:String,defaultValue:Boolean):Boolean
    private external fun removeValueFromKey(handle: Long,key:String)
    private external fun containsKey(handle: Long,key:String):Boolean
    private external fun putBytes(handle: Long,key:String,value:ByteArray?)
    private external fun getBytes(handle: Long,key:String):ByteArray?
    private external fun clear(handle: Long)

}